/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package cn.ekuma.data.dao.user;

import cn.ekuma.data.dao.I_DataLogic;
import cn.ekuma.data.dao.bean.I_BaseBean;
 import java.util.*;
import javax.swing.ListModel;
import javax.swing.event.EventListenerList;
import javax.swing.event.ListDataEvent;
import javax.swing.event.ListDataListener;
import com.openbravo.data.basic.BasicException;
import com.openbravo.data.loader.LocalRes;
import com.openbravo.data.user.EditorCreator;
import com.openbravo.data.user.Finder;

/**
 *
 * @author Administrator
 */
public class BrowsableDataModel implements ListModel {

    protected EventListenerList listeners = new EventListenerList();
    private boolean m_bIsAdjusting;

    private List<I_BaseBean> m_aData; // List<Object> 

    private Comparator m_comparer;

    private I_DataLogic  dataLogic;

    private Class c;

    private EditorCreator filterCreator;

    /** Creates a new instance of BrowsableData */
    public BrowsableDataModel(I_DataLogic dataLogic,Class cl, Comparator c,EditorCreator filterCreator) {
        this.dataLogic=dataLogic;
        m_comparer = c;
        m_bIsAdjusting = false;
        this.c=cl;
        this.filterCreator=filterCreator;
        m_aData = new ArrayList();
    }

    public BrowsableDataModel(I_DataLogic dataLogic,Class cl, Comparator c) {
       this(dataLogic,cl,c,null);
    }

    public BrowsableDataModel(I_DataLogic dataLogic,Class cl,EditorCreator filterCreator) {
        this(dataLogic,cl,null,filterCreator);
     }

     public BrowsableDataModel(I_DataLogic dataLogic,Class cl) {
        this(dataLogic,cl,null,null);
     }


    public final void addListDataListener(ListDataListener l) {
        listeners.add(ListDataListener.class, l);
    }
    public final void removeListDataListener(ListDataListener l) {
        listeners.remove(ListDataListener.class, l);
    }

    // Metodos de acceso
    public final I_BaseBean getElementAt(int index) {
        return m_aData.get(index);
    }

    public final int getSize() {
        return m_aData.size();
    }

    public final boolean isAdjusting() {
        return m_bIsAdjusting;
    }

    protected void fireDataIntervalAdded(int index0, int index1) {
        m_bIsAdjusting = true;
        EventListener[] l = listeners.getListeners(ListDataListener.class);
        ListDataEvent e = null;
        for (int i = 0; i < l.length; i++) {
            if (e == null) {
                e = new ListDataEvent(this, ListDataEvent.INTERVAL_ADDED, index0, index1);
            }
            ((ListDataListener) l[i]).intervalAdded(e);
        }
        m_bIsAdjusting = false;
    }
    
    protected void fireDataContentsChanged(int index0, int index1) {
        m_bIsAdjusting = true;
        EventListener[] l = listeners.getListeners(ListDataListener.class);
        ListDataEvent e = null;
        for (int i = 0; i < l.length; i++) {
            if (e == null) {
                e = new ListDataEvent(this, ListDataEvent.CONTENTS_CHANGED, index0, index1);
            }
            ((ListDataListener) l[i]).contentsChanged(e);
        }
        m_bIsAdjusting = false;
    }
    protected void fireDataIntervalRemoved(int index0, int index1) {
        m_bIsAdjusting = true;
        EventListener[] l = listeners.getListeners(ListDataListener.class);
        ListDataEvent e = null;
        for (int i = 0; i < l.length; i++) {
            if (e == null) {
                e = new ListDataEvent(this, ListDataEvent.INTERVAL_REMOVED, index0, index1);
            }
            ((ListDataListener) l[i]).intervalRemoved(e);
        }
        m_bIsAdjusting = false;
    }

    public void refreshData() throws BasicException {
       if(filterCreator==null)
          putNewData(dataLogic.list(c));
        else
         putNewData(dataLogic.listByFilter(c, filterCreator.createValue()));
    }

    public void loadData() throws BasicException {
         refreshData();
    }

    public void unloadData() throws BasicException {
        putNewData(null);
    }
    public void loadList(List l) {
        putNewData(l);
    }

    public void sort(Comparator c) throws BasicException {
        Collections.sort(m_aData, c);
        putNewData(m_aData);
    }

    public final boolean canLoadData() {
        return dataLogic != null ;
    }

    public boolean canInsertData() {
        return dataLogic != null ;
    }

    public boolean canDeleteData() {
        return dataLogic != null ;
    }

    public boolean canUpdateData() {
        return dataLogic != null ;
    }

    public final int findNext(int index, Finder f) throws BasicException {
        int i = index + 1;

        // search up to the end of the recordset
        while (i < m_aData.size()) {
            if (f.match(this.getElementAt(i))) {
                return i;
            }
            i++;
        }
        // search from the begining
        i = 0;
        while (i < index) {
            if (f.match(this.getElementAt(i))) {
                return i;
            }
            i++;
        }

        // No se ha encontrado
        return -1;
    }

    public final int removeRecord(int index) throws BasicException {
        if (canDeleteData() && index >= 0 && index < m_aData.size()) {
            if (dataLogic.delete(m_aData.get(index)) > 0) {
                // borramos el elemento indicado
                m_aData.remove(index);
                // disparamos los eventos
                fireDataIntervalRemoved(index, index);

                int newindex;
                if (index < m_aData.size()) {
                    newindex = index;
                } else {
                    newindex = m_aData.size() - 1;
                }
                return newindex;
            } else {
                throw new BasicException(LocalRes.getIntString("exception.nodelete"));
            }
        } else {
            // indice no valido
            throw new BasicException(LocalRes.getIntString("exception.nodelete"));
        }
    }

    public final int updateRecord(int index, I_BaseBean value) throws BasicException {

        if (canUpdateData() && index >= 0 && index < m_aData.size()) {
            if (dataLogic.update(value) > 0) {
                // Modificamos el elemento indicado
                int newindex;
                if (m_comparer == null) {
                    newindex = index;
                    m_aData.set(newindex, value);
                } else {
                    // lo movemos
                    newindex = insertionPoint(value);
                    if (newindex == index + 1) {
                        newindex = index;
                        m_aData.set(newindex, value);
                    } else if (newindex > index + 1) {
                        m_aData.remove(index);
                        newindex --;
                        m_aData.add(newindex, value);
                    } else {
                        m_aData.remove(index);
                        m_aData.add(newindex, value);
                    }
                }

                if (newindex >= index) {
                    fireDataContentsChanged(index, newindex);
                } else {
                    fireDataContentsChanged(newindex, index);
                }
                return newindex;
            } else {
                // fallo la actualizacion
                throw new BasicException(LocalRes.getIntString("exception.noupdate"));
            }
        } else {
            // registro invalido
            throw new BasicException(LocalRes.getIntString("exception.noupdate"));
        }
    }
    public final int insertRecord(I_BaseBean value) throws BasicException {
        if (canInsertData() && dataLogic.insert(value) > 0) {
            int newindex;
            if (m_comparer == null) {
                // Anadimos el elemento indicado al final...
                newindex = m_aData.size();
             } else {
                 // lo insertamos en el lugar adecuado
                newindex = insertionPoint(value);
             }
             m_aData.add(newindex, value);

            // Disparamos la inserccion
            fireDataIntervalAdded(newindex, newindex);
            return newindex;
        } else {
            throw new BasicException(LocalRes.getIntString("exception.noinsert"));
        }
    }

    private final void putNewData(List<I_BaseBean> aData) {

        int oldSize = m_aData.size();
        m_aData = (aData == null) ? new ArrayList() : aData;
        int newSize = m_aData.size();

        // Ordeno si es un Browsabledata ordenado
        if (m_comparer != null) {
            Collections.sort(m_aData, m_comparer);
        }

        fireDataContentsChanged(0, newSize - 1);
        if (oldSize > newSize) {
            fireDataIntervalRemoved(newSize, oldSize - 1);
        } else if (oldSize < newSize) {
            fireDataIntervalAdded(oldSize, newSize - 1);
        }
    }

    private final int insertionPoint(I_BaseBean value) {

	int low = 0;
	int high = m_aData.size() - 1;

	while (low <= high) {
	    int mid = (low + high) >> 1;
	    I_BaseBean midVal = m_aData.get(mid);
	    int cmp = m_comparer.compare(midVal, value);

	    if (cmp <= 0) {
		low = mid + 1;
            } else {
		high = mid - 1;
            }
	}
	return low;
    }
}
